#include "CNode.h"
#include "CConsole.h"
#include "CNetwork.h"
#include "argslib.h"
#include "CSendMess.h"
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

extern std::string pathTcl;
extern CNetwork *net;
extern pthread_mutex_t MX_RWNet;
extern pthread_mutex_t MX_RWMess;

extern std::vector< CSendMess* > v_sendMess;
extern pthread_mutex_t mutex;

extern int i_winWidth;
extern int i_winHeight;

void *listenNode(void *p_node)
{
	//printf(";11;;%s;;;\n",((CNode *)p_node)->getName());
	int i = 0;

	char receive[800];
	CNode *node = (CNode *)p_node;
	std::string line;
	std::stringstream stream;
	int tpsTransfert = 0;
	//printf(";10;;%s;;;\n",node->getName());
	while(1)
	{
		i = read(node->out[0], receive, 800);
		receive[i] = '\0';
		stream.clear();
		stream << receive;

		//fprintf(stdout, "father received message: -%s- \n", receive);
pthread_mutex_lock(&MX_RWNet);
pthread_mutex_lock(&MX_RWMess);
		while(getline(stream,line,'\n'))
		{

			//printf("-%s-\n",line.c_str());
			for(int j=0; j<node->nbNeighbours(); j++)
			{
				std::string s(line.c_str());
				s+="\n";
				//write(node->getNeighbour(j)->in[1], s.c_str(), s.size());
				pthread_mutex_lock(&mutex);
				tpsTransfert = (int)node->getNeighbourDist(j)/10;
				v_sendMess.push_back(new CSendMess(1+(int)node->getCharge()+tpsTransfert,node,node->getNeighbour(j),s));
				pthread_mutex_unlock(&mutex);

			}
		}
pthread_mutex_unlock(&MX_RWMess);
pthread_mutex_unlock(&MX_RWNet);
	}
}

std::vector < pthread_t * > fils_thread;
std::vector < int > fils_pidAppli;

void remNode(std::string name){
	CNode *node;
	int i;
	//std::cout << "ho" << std::endl;
	pthread_mutex_lock(&MX_RWNet);
	
	if((i = net->getNumNodeByName(name)) != -1){
		//std::cout << "hop" << std::endl;
		std::vector< pthread_t * >::iterator fils_threadIt = fils_thread.begin() + i;
		
		//std::cout << "hop1" << std::endl;
		std::vector< int >::iterator fils_pidAppliIt = fils_pidAppli.begin() + i;
		
		//pthread_terminate(fils_thread[i]);
		//std::cout << "hop2 " << fils_thread.size() << " " << i << std::endl;
		fils_thread.erase(fils_threadIt);
		
		//std::cout << "hop3" << std::endl;
		//std::cout << "hop4 " << fils_pidAppli.size() << " " << i << std::endl;
		kill(fils_pidAppli[i],9);
		fils_pidAppli.erase(fils_pidAppliIt);
		//std::cout << "hop5" << std::endl;
	}
	net->remNode(name);
	pthread_mutex_unlock(&MX_RWNet);
}

void remNodes(){
	int i;
	while(net->nbNodes()>0){
		remNode(net->getNode(0)->getName());
	}
}

void addNode(std::string name, double p_d_x=100, double p_d_y=100, int p_i_posX=-1, int p_i_posY=-1)
{
	pthread_t *filsA;
	filsA = (pthread_t *) malloc (sizeof(pthread_t));
	fils_thread.push_back(filsA);
	
	CNode *node = new CNode(p_d_x, p_d_y, name.c_str());

	

	pipe(node->in);
	pipe(node->out);


			if (pthread_create(filsA, NULL, listenNode, node))
		{
			perror("pthread_create");
			exit(EXIT_FAILURE);
		}
		pthread_mutex_lock(&MX_RWNet);
		net->addNode(node);
pthread_mutex_unlock(&MX_RWNet);
	
	int ii=0;
	int i = 0;
	
	int pid1;
	char receive[80];
	//char *gets();
	//pthread_t *filsA;
	//printf(";2;;%s;;;\n",node->getName());
int pid=fork();
	//printf(";3;;%s;;;\n",node->getName());
		if(pid==-1){ printf("error fork\n"); return;}
	if (pid == 0)									 /* CHILD process */
	{

		dup2(node->in[0],0);
		close(node->in[1]);
		dup2(node->out[1],1);
		close(node->out[0]);

    if(p_i_posX == -1){
      //fprintf(stderr,"pid: %i\n", getpid());
			
			execl("/usr/bin/wish","wish",pathTcl.c_str(),node->getName(),NULL);
    }else{
      std::stringstream position;
      position << "+";
      position << p_i_posX;
      position << "+";
      position << p_i_posY;
			
      execl("/usr/bin/wish","wish",pathTcl.c_str(),node->getName(),position.str().c_str(),NULL);
    }

	}

	else													 /* Parent process */
	{
		//printf(";6;;%s;;;\n",node->getName());
fils_pidAppli.push_back(pid);

		//printf(";5;;%s;;;\n",node->getName());

	}
}


void openNetwork(char *p_sz_fileName)
{
  //pthread_mutex_lock(&MX_RWNet);
	int i_nbNodes;
	double d_x, d_y;
	int temp;
	std::string str_name;
	CNode *node;

	std::ifstream file(p_sz_fileName);
	if(!file){return;}

	file >> i_nbNodes;

	for(int k=0; k<i_nbNodes; k++)
	{
		file >> str_name >> d_x >> d_y;
		//node = new CNode(d_x, d_y,str_name.c_str());
		addNode(str_name, d_x, d_y);
	}
	
	file.close();
  //pthread_mutex_unlock(&MX_RWNet);
}

void saveNetwork(char *p_sz_fileName)
{
  pthread_mutex_lock(&MX_RWNet);
	int i_nbNodes;
	double d_x, d_y;
	int temp;
	std::string str_name;
	CNode *node;

	std::ofstream file(p_sz_fileName);
	if(!file){return;}

	file << net->nbNodes() << std::endl;

	for(int k=0; k<net->nbNodes(); k++)
	{
		file << net->getNode(k)->getName();
		file << " " << net->getNode(k)->getX();
		file << " " << net->getNode(k)->getY();
		file << std::endl;
	}
	
	file.close();
  pthread_mutex_unlock(&MX_RWNet);
}

void quit(){
	
}

//---------------------------------------------------------------------------//
// Console
//---------------------------------------------------------------------------

CConsole::CConsole() : m_Current("")
{
  // Init alias
  std::string str_alias, name, com;

  std::ifstream file("cmd_alias");
  if(!file){
  }else{

    while(!file.eof()){
      getline(file,str_alias);

      printf("> Definition alias: \"%s\"\n",str_alias.c_str());
      name=str_alias.substr(0,str_alias.find_first_of("="));

      com=str_alias.substr(str_alias.find_first_of("=")+1);
      alias.push_back(new CAlias(name, com));
    }
  }
  // Fin init alias

  cmds.push_back(new cmd("help","",0));
  cmds.push_back(new cmd("node","<id> [-p*os <x> <y>]",1));
	cmds.push_back(new cmd("rem","<id>",9));
  //cmds.push_back(new cmd("test","<p5> [-p2 <v2>] [[/p3 [v3]] [/p4]]",2));
  cmds.push_back(new cmd("centre","<id du centre> [-r*emove]",3));
  cmds.push_back(new cmd("export","<nom de fichier>",4));
	cmds.push_back(new cmd("save","<nom de fichier>",8));
  cmds.push_back(new cmd("open","<nom de fichier>",7));
  cmds.push_back(new cmd("charge","<id> <charge>",5));
  cmds.push_back(new cmd("nodes","<prefixe> <nombre de noeud> [-p]",6));
	cmds.push_back(new cmd("exit","",10));

  m_i_history=0;
}


//std::vector <cmd *> cmds;

//---------------------------------------------------------------------------
// Ajoute du texte dans la console
void CConsole::addConsole(std::string text)
{
	vconsole.push_back(new std::string(text));
}

void CConsole::addLastCmd(std::string text)
{
  lastCmds.push_back(new std::string(text));
}


//---------------------------------------------------------------------------
// Execute la commande
int CConsole::ProcessCurrent(std::string argv)
{
	int i=0;
  int i_return=0;
	cmd *c;

  bool found=false;
  while(i<alias.size() && !found){
    if(alias[i]->name.compare(argv)==0) argv=alias[i]->cmd;
    i++;
  }
  i=0;
	std::string cmd_name = argv.substr(0,argv.find_first_of(" "));

	while(i<cmds.size() && cmds[i]->name.compare(cmd_name) != 0)
	{
		i++;
	}

	if(i==cmds.size())
	{
		addConsole("commande "+cmd_name+" inconnue");
		return -1;
	}
	c = cmds[i];

	CCommandLineParameters clp((char *)argv.c_str());
	// Demande d'aide
	//if (clp.CheckHelp(true)) {addConsole(c->getUsage());return -1;}

	switch(c->type)
	{
		case 0:{
      std::string str_help;
      for(int i=1; i<cmds.size(); i++){
        //str_help += cmds[i]->getUsage();
        addConsole(cmds[i]->getUsage());
      }
//      addConsole(str_help.c_str());
      addLastCmd(argv);
      return 0;
    }
      break;
		case 10:{
      remNodes();
			exit(0);
    }
      break;
    case 1: // Ajout d'un noeud
    {
      std::string id;
      double dx, dy;

      if (!clp.FirstNonSwitchIndex(1)) {addConsole(c->getUsage());return -1;}

      id = clp.GetNonSwitchStr(1);
			bool p2    = clp.Switch("p*os")>0;
			std::string v2 = clp.GetSwitchStr("p*os",0);
			std::string v3 = clp.GetSwitchStr("p*os",1);

			if (p2>0 && v2.empty()) {addConsole(c->getUsage());return -1;}

			
			if(v2.empty() || v3.empty())
			{
				addNode(id);
			}
			else
			{
				addNode(id,atof(v2.c_str()),atof(v3.c_str()));
			}
		}break;
		case 2:											 // Test
		{/*
			//---------------------------------------------------------
			// Syntax checking for p1.
			// First parameter must be a non-switch
			//---------------------------------------------------------
			if (clp.FirstNonSwitchIndex() != 1)
			{
				printf("! syntax error: p1 required\n");
				addConsole(c->getUsage());
				return -1;
			}

			CString p1 = clp.FirstNonSwitchStr();
			//---------------------------------------------------------
			// Syntax checking for p2. If p2 is used, then
			// the next argument must be a non-switch.
			//---------------------------------------------------------

			BOOL p2    = clp.Switch("p2")>0;
			CString v2 = clp.GetSwitchStr("p2",0);

			if (p2 && v2.empty())
			{
				printf("! syntax error: v2 required when /p2 is used\n");
				addConsole(c->getUsage());
				return -1;
			}

			//---------------------------------------------------------
			// Syntax checking for p2.
			// For p3, since v3 is optional, we use a default "" value.
			// p4 is only valid if p3 is enabled
			//---------------------------------------------------------

			BOOL p3    = clp.Switch("p3")>0;
			CString v3 = clp.GetSwitchStr("p3",0,"");
			BOOL p4    = p3 && (clp.Switch("p4")>0);

			printf("p1 : %s\n",p1.c_str());
			printf("p2 : %d  v2: %s\n",p2,v2.c_str());
			printf("p3 : %d  v3: %s\n",p3,v3.c_str());
			printf("p4 : %d\n",p4);*/
		}break;
		case 3:											 // Ajout d'un centre
		{
			std::string id;

			if (clp.FirstNonSwitchIndex(1)==-1) {addConsole(c->getUsage());return -1;}
			id = clp.GetNonSwitchStr(1);
			BOOL r = (clp.Switch("r*emove")>0);
			net->addCentre(id, r);
		}break;
		case 4:											 // Sauvegarde au format svg
		{
			std::string filename;

			if (clp.FirstNonSwitchIndex(1)==-1) {addConsole(c->getUsage());return -1;}
			filename = clp.GetNonSwitchStr(1);
			net->filesav(filename);
		}break;
		case 7:											 // Ouverture d'un network
		{
			std::string filename;

			if (clp.FirstNonSwitchIndex(1)==-1) {addConsole(c->getUsage());return -1;}
			filename = clp.GetNonSwitchStr(1);
			openNetwork((char *)filename.c_str());
		}break;
		case 8:											 // Sauvegarde d'un network
		{
			std::string filename;

			if (clp.FirstNonSwitchIndex(1)==-1) {addConsole(c->getUsage());return -1;}
			filename = clp.GetNonSwitchStr(1);
			saveNetwork((char *)filename.c_str());
		}break;
		case 9:											 // Suppression d'un noeud
		{
			std::string id;

			if (clp.FirstNonSwitchIndex(1)==-1) {addConsole(c->getUsage());return -1;}
			id = clp.GetNonSwitchStr(1);
			remNode((char *)id.c_str());
		}break;
		case 5:											 // Modification de la charge d'un noeud
		{
			double charge;
			std::string name;
			CNode *node;

			if (clp.FirstNonSwitchIndex(1)==-1 || clp.FirstNonSwitchIndex(2)==-1) {addConsole(c->getUsage());return -1;}
			charge = atof(clp.GetNonSwitchStr(2,false,true).c_str());
			name = clp.GetNonSwitchStr(1);
			//printf(">>>>name -%s-\n",name.c_str());
			//net->filesav(filename);
pthread_mutex_lock(&MX_RWNet);
			node = net->getNodeByName(name);


			if(node != NULL) {node->setCharge(charge); printf("change charge\n");}
pthread_mutex_unlock(&MX_RWNet);
		}break;
    case 6: // Ajout de n noeuds
    {
      int i_nbNode;
      std::string name;

      if (clp.FirstNonSwitchIndex(1)==-1 ||  clp.FirstNonSwitchIndex(2)==-1)
      {addConsole(c->getUsage());return -1;}
      i_nbNode = atoi(clp.GetNonSwitchStr(2,false,true).c_str());
      name = clp.GetNonSwitchStr(1);
      bool p = clp.Switch("p")>0;
int decPosX, decPosY;
decPosX = (int)(1204/(int)sqrt(i_nbNode+1));
decPosY = (int)(768/(int)sqrt(i_nbNode+1));
int posX=decPosX, posY=0;

	srand(time(NULL));

      for(int i=0; i<i_nbNode; i++){
        std::stringstream nameExt;
        usleep(100000);
        //nameExt.clear();

usleep(100000);
        nameExt << name;
        nameExt << "_" << i;
//char buf[255];
        //nameExt << i;//+= "_" + std::string(i);


//printf(">>>> %i\n",i_winWidth);
if(!p){
        addNode(nameExt.str(),
          (double)i_winWidth * (double)rand() / (double)(RAND_MAX),
          (double)i_winHeight * (double)rand() / (double)(RAND_MAX));
}else{
        addNode(nameExt.str(),
          (double)i_winWidth * (double)rand() / (double)(RAND_MAX),
          (double)i_winHeight * (double)rand() / (double)(RAND_MAX),
        posX-100, posY-50);
posX+=decPosX;
if(posX>=1024){posX=0;posY+=decPosY;}
}
      }
    }break;
	}
	addConsole(argv);
  addLastCmd(argv);
  return 0;
}

void CConsole::setHistory(int p_i_dec){
  int i_newPos = m_i_history + p_i_dec;
  if(i_newPos >= 0 && i_newPos<=lastCmds.size()-1){
    m_Current=*lastCmds[i_newPos];
    m_i_history = i_newPos;
  }else if(i_newPos == lastCmds.size()){
    m_Current.clear();
    m_i_history = i_newPos;
  }
}

//----------------------------------------------------------
// Envoie un nouveau caractere a la console
void CConsole::SendChar(char Character)
{
	// Si la console n'est pas active on ne traite pas le caractre

	// Traitement du caractre
	switch (Character)
	{
		// Saut de ligne : on traite la commande et on efface la ligne
		case '\n' :
		case '\r' :
			if (!m_Current.empty())
			{
				if(!ProcessCurrent(m_Current)){
          m_i_history = lastCmds.size();
				  m_Current.clear();
        }
			}
			break;

			// Backspace : on efface le dernier caractre
		case '\b' :
			if (!m_Current.empty())
				m_Current.erase(m_Current.size() - 1);
			break;

			// Tout le reste : on ajoute le caractre  la ligne courante
		default :
			m_Current += Character;
			break;
	}
}
